package com.ruoyi.cart.controller;


import com.ruoyi.cart.domain.CartVO;
import com.ruoyi.cart.service.IPmsCartService;
import com.ruoyi.cart.service.IPmsCartSkuService;
import com.ruoyi.common.core.domain.Result;
import com.ruoyi.common.core.web.controller.BaseController;

import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.common.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.*;
import org.springframework.util.StringUtils;

import javax.annotation.PreDestroy;
import java.util.concurrent.ThreadLocalRandom;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.Executors;

/**
 * 购物车Controller
 *
 * @author jorker
 * @date 2024-12-27
 */
@RestController
@RequestMapping("/")
public class PmsCartController extends BaseController
{

    @Autowired
    private IPmsCartService pmsCartService;
    @Autowired
    private IPmsCartSkuService pmsCartSkuService;
    @Autowired
    private RedisTemplate redisTemplate;

    private final ScheduledExecutorService watchDogExecutor = Executors.newScheduledThreadPool(1);

    /**
     * 获取选中的商品
     */
    @GetMapping("/listCheck")
    public Result listCheck() {
        List<CartVO> cartVOList = pmsCartService.listCheck();
        return Result.success(cartVOList);
    }
    /**
     * 获取登录人购物车列表
     */
    @GetMapping("/list")
    public Result list() {
        List<CartVO> cartVOList = pmsCartService.showCart();
        return Result.success(cartVOList);
    }

    @PutMapping("/updateSkuCount/{skuId}/{count}")
    public Result updateSkuCount(@PathVariable Long skuId, @PathVariable Long count){
        pmsCartService.updateSkuCount(skuId,count);
        return Result.success();
    }
    @PutMapping("/updateCheck/{skuId}/{checked}")
    public Result updateCheck(@PathVariable Long skuId, @PathVariable Boolean checked){
        pmsCartService.updateCheck(skuId,checked);
        return Result.success();
    }


    @DeleteMapping("/removePayed")
    public Result removePayed(@RequestParam("skuIds") Long[] skuIds){
        pmsCartService.removePayed(skuIds);
        return Result.success();
    }

    @DeleteMapping("/removeSkuFromCart/{skuId}")
    public Result removeSkuFromCart(@PathVariable Long skuId){
        pmsCartService.removeSkuFromCart(skuId);
        return Result.success();
    }


    @PostMapping("/addSkuInCart/{skuId}/{count}")
    public Result addSkuInCart(@PathVariable Long skuId, @PathVariable Long count){
        pmsCartService.addSkuInCart(skuId,count);
        return Result.success();
    }

    @GetMapping("/getCartSku/{skuId}")
    public Result getCartSku(@PathVariable Long skuId) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long userId = loginUser.getUserid();

        String goodsKey = "goods:" + skuId;
        String userKey = "user:" + userId;
        String lockKey = "lock:cart:" + skuId;
        String bloomKey = "bloom:goods";
        String soldKey = "sold:" + skuId;

        ScheduledFuture<?> watchDogFuture = null;

        try {
            // 布隆过滤器校验...
            Boolean exists = (Boolean) redisTemplate.execute(
                new DefaultRedisScript<>(
                    "return redis.call('BF.EXISTS', KEYS[1], ARGV[1])",
                    Boolean.class
                ),
                Collections.singletonList(bloomKey),
                skuId.toString()
            );

            if (Boolean.FALSE.equals(exists)) {
                return Result.error("商品不存在");
            }

            String stockStr = (String) redisTemplate.opsForValue().get(goodsKey);

            if (!StringUtils.hasText(stockStr)) {
                String lockValue = UUID.randomUUID().toString();
                Boolean acquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);

                if (Boolean.TRUE.equals(acquired)) {
                    try {
                        // 启动看门狗
                        watchDogFuture = watchDogExecutor.scheduleAtFixedRate(() -> {
                            String script =
                                "if redis.call('get',KEYS[1]) == ARGV[1] then " +
                                "   return redis.call('expire',KEYS[1],30) " +
                                "else " +
                                "   return 0 " +
                                "end";

                            try {
                                redisTemplate.execute(
                                    new DefaultRedisScript<>(script, Long.class),
                                    Collections.singletonList(lockKey),
                                    lockValue
                                );
                            } catch (Exception e) {
                                // 记录日志但不中断看门狗
                                e.printStackTrace();
                            }
                        }, 10, 10, TimeUnit.SECONDS);  // 每10秒续期一次

                        // 双重检查
                        stockStr = (String) redisTemplate.opsForValue().get(goodsKey);
                        if (!StringUtils.hasText(stockStr)) {
                            // TODO: 从数据库查询库存
                            Long stock = 100L;

                            int randomExpire = 3600 + ThreadLocalRandom.current().nextInt(300);
                            redisTemplate.opsForValue().set(goodsKey, stock.toString(), randomExpire, TimeUnit.SECONDS);
                            stockStr = stock.toString();
                        }
                    } finally {
                        // 停止看门狗
                        if (watchDogFuture != null) {
                            watchDogFuture.cancel(true);
                        }

                        // 释放锁
                        String unlockScript =
                            "if redis.call('get',KEYS[1]) == ARGV[1] then " +
                            "   return redis.call('del',KEYS[1]) " +
                            "else " +
                            "   return 0 " +
                            "end";

                        redisTemplate.execute(
                            new DefaultRedisScript<>(unlockScript, Long.class),
                            Collections.singletonList(lockKey),
                            lockValue
                        );
                    }
                } else {
                    Thread.sleep(100);
                    return getCartSku(skuId);
                }
            }

            // 优化后的库存扣减和购物车添加脚本
            String script =
                "local stock = tonumber(redis.call('get', KEYS[1])) " +  // 当前库存
                "local sold = tonumber(redis.call('get', KEYS[3]) or '0') " +  // 已售数量
                "local total = tonumber(ARGV[2]) " +  // 总库存

                "if stock == nil then " +
                "   return -1 " +  // 库存key不存在
                "end " +

                "if sold >= total then " +
                "   return -2 " +  // 超过总库存限制
                "end " +

                "if stock <= 0 then " +
                "   return 0 " +  // 库存不足
                "end " +

                // 增加库存校验
                "if (stock + sold) > total then " +
                "   redis.call('set', KEYS[1], total - sold) " +  // 修正库存
                "   return -3 " +  // 库存数据异常
                "end " +

                 //扣减库存并记录售出
                "redis.call('decr', KEYS[1]) " +
                "redis.call('incr', KEYS[3]) " +
                "redis.call('lpush', KEYS[2], ARGV[1]) " +

                "return 1";  // 成功

            Long result = (Long) redisTemplate.execute(
                new DefaultRedisScript<>(script, Long.class),
                Arrays.asList(goodsKey, userKey, soldKey),
                userId.toString(),
                "100"  // 总库存限制
            );

            switch(result.intValue()) {
                case 1:
                    return Result.success("商品加入购物车成功");
                case 0:
                    return Result.error("商品库存不足");
                case -1:
                    return Result.error("商品库存数据不存在");
                case -2:
                    return Result.error("商品已售罄");
                case -3:
                    return Result.error("库存数据异常，已自动修正");
                default:
                    return Result.error("未知错误");
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return Result.error("系统繁忙，请稍后重试");
        } catch (Exception e) {
            return Result.error("系统异常: " + e.getMessage());
        } finally {
            if (watchDogFuture != null) {
                watchDogFuture.cancel(true);
            }
        }
    }

    @PreDestroy
    public void destroy() {
        watchDogExecutor.shutdown();
    }

}
